home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr53 / 312_01.zip / CINCDEP.AWK < prev    next >
Text File  |  1993-04-22  |  16KB  |  336 lines

  1. #  HEADER:        ;
  2. #  TITLE:         Makefile dependency generator;
  3. #  DATE:          09/28/1989;
  4. #  VERSION:       2.2;
  5. #  DESCRIPTION:   "An AWK program which finds included files and
  6. #                 builds a dependency list.  This dependency list
  7. #                 is a component of a make-file.  The list consists
  8. #                 of the source file name followed by a colon and
  9. #                 then all of the files which it includes (and also
  10. #                 those which are included by the included files, &c.)
  11. #                 The last item is a compile-command.";
  12. #
  13. #  KEYWORDS:      Makefile, make, include, dependency generator;
  14. #  SYSTEM:        MS-DOS, UNIX;
  15. #  WARNINGS:      "Doesn't handle directory names embedded within
  16. #                 #include <here>.  Doesn't handle preprocessor #if.. blocks
  17. #                 or #include statements which are "commented out."
  18. #                 Only handles a single include-path.  
  19. #                 PolyAwk will generate false matches to lines
  20. #                 beginning with non-ASCII (i.e. 128..256) chars."
  21. #                 Filenames are case sensitive.";
  22. #  FILENAME:      CINCDEP.AWK;
  23. #  SEE-ALSO:      TLR2MAK.AWK, CF2MAK.AWK, AINCDEP.AWK, PINCDEP.AWK;
  24. #  AUTHORS:       James Yehle;
  25. #  COMPILERS:     PolyAWK, Mortice Kern AWK, Rob Duff's PC-AWK 2.0,
  26. #                 In any case, must be 1985 awk (not 1977);
  27. #
  28. # ============================================================================
  29. #
  30. # cincdep.awk         Last modified  Sep 28, 1989  22:23
  31. #
  32. #   Scans a C file for all "#include" dependencies
  33. #   This scanning process involves all nested include files, too.
  34. #
  35. #  Jim Yehle  753 Left Fork Rd. #B/Sugarloaf Star Route/Boulder, CO 80302 9252
  36. # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  37. #
  38. #   Usage is:
  39. #     awk -f [path]cincdep.awk [path]src_file.c objname=[path]filename
  40. #        cc=compile_comd ccf=[p][n][e] [incpath=[pathname]] [>[path]outfile]
  41. #     Pathnames must contain trailing separator, e.g. "c:\inc\"
  42. #     Order of command-line parameters (save for "-f ..." and srcfile.c)
  43. #      is irrelevant, but identifiers must be in lower case.
  44. #     "src_file.c" may be replaced by special "__NOFILE__"
  45. #     cc is the compile command (no spaces allowed)
  46. #     ccf controls which components of the source file's name get
  47. #         included in the compile command line: p = directory path,
  48. #         n = name, e = extension.
  49. #
  50. #   Output is:
  51. #     [path]src_file.obj: [path]src_file.c [path]included_file1 linecont
  52. #                         [path]included_file2 [path]included_file3 ...
  53. #     <\t>comd [path]src_file[extenstion]
  54. #     <\n>
  55. #   For __NOFILE__, output is:
  56. #     [path]src_file.obj:
  57. #     <\n>
  58. #     <\n>
  59. # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  60. #
  61. # Known limitations and anomalies:
  62. # - Assumes that no directory paths are within the "file" or <file> strings
  63. # - Doesn't make any attempt to switch out #if-#endif blocks.
  64. #    I don't see this as a serious problem.  (The solution certainly isn't
  65. #    trivial.)  It will mean only that your make-file may turn out to be a
  66. #    bit more "paranoid," including more dependencies than really exist
  67. #    if an #include directive is #iffed out.  It could be a problem if 
  68. #    nonexistent include-files appear in a make-file's dependency, and your
  69. #    make utility barfs when it doesn't find a file.  
  70. # - The same problem can be created by "commenting out" a line containing
  71. #    an include directive, such as:
  72. #       /*
  73. #       #include <stdio.h>
  74. #       */
  75. # - File names are case- and directory-sensitive.  Huh?  If a file includes
  76. #    both blah.h and Blah.h, they'll both appear in the dependency list.
  77. #    Furthermore, if it includes "splunge.h" and <splunge.h>, and an instance
  78. #    appears in both the default directory and the include-file directory,
  79. #    you'll get two occurrences in the dependency list, one with the 
  80. #    directory pathname tacked on the front and the other without.
  81. # - So far it has been tested with Polytron and MKS awk interpreters, and
  82. #    Turbo C's make utility, all running under the MS-DOS operating system.
  83. # - Your ability to specify complex compilation commands is really limited.
  84. #    There's also no way to set source-file-specific command-line options.
  85. # - I don't know what will happen if your source file doesn't end with .c
  86. #    and your object files don't end with .obj.  Try it!
  87. # - Only one include-path is supported.  For the form #include <file>, cincdep
  88. #    searches the include-path first, then the current directory for the
  89. #    given file.  If your file says #include "file", the order is reversed.
  90. #
  91. # . . . Revision history . . . . . . . . . . . . . . . . . . . . . . . . . . .
  92. #
  93. #        Sep 28  89            -- Released for C User's Group distribution --
  94. #  2.2   Sep 10  89       JRY  Added legal-filename-char regular expression
  95. #  2.1   Aug 17  89       JRY  Added add_fnexist option (as internal directive)
  96. #  2.0   Aug 10  89       JRY  If an included file can't be found, cincdep now
  97. #                               beeps and prints an error to CO, then continues
  98. #                               without scanning the file for nested inclusions.
  99. #                               (It adds it to the dependency list, without a
  100. #                               directory path on the front of the filename.)
  101. #                               Also added an optimization:  if addfile() sees
  102. #                               that a file is already in the list, then it has
  103. #                               already been scanned, so it isn't done again.
  104. #                               Added "verbose" CO output as debug level 1.
  105. #  1.7   Jun 20  89       JRY  Added "ccf=" command-line parameter
  106. #  1.6   Jun 7   1989     JRY  Added include-file pathnames to output,
  107. #                               added objname= & cc= command-line parameters
  108. #  1.5   May 31  1989     JRY  Added primary source file at start of
  109. #                               dependency list
  110. #  1.4  Cinco di Mayo 89  JRY  Cleaned output-line-too-long test
  111. #  1.3   Apr 21  1989     JRY  Added line-too-long continuation on next line,
  112. #                               translation of primary file from .c to .obj.
  113. #  1.2   Feb 22  1989     JRY  Added incpath command-line option
  114. #  1.1   Feb 21  1989     JRY  Re-wrote to handle recursion internally.
  115. #  1.0   Feb 15  1989     JRY  Initial creation & debugging.. algorithm used
  116. #                               recursive invocations of awk via system(awk)
  117. #                               function, but ran out of memory (640k DOS)
  118. #                               after three levels.
  119. # ============================================================================
  120. #
  121.  
  122. BEGIN {
  123.    co = "CON"       # Console-out: MS-DOS "CON", iRMX ":co:", unix "/dev/tty"
  124.    linecont = " \\" # Line-continuation EOL char: snake " /", unix make "\\"
  125.    debug = 0        # 0=off, 1=verbose, 2=main-level debug, 3= adds functions
  126.    use_fnexist = 1  # Add nonexistent header files to dependency list?
  127.    fname_chars = "[^\\\\ :]" # Any single legal char in a file name.
  128.    #  (Must exclude directory path separator character! (DOS \, UNIX /) )
  129.  
  130.    printf( "cincdep.awk  C #include dependency scanner  v 2.2") > co
  131.    objfile = get_cl_param( "objname=", 0)
  132.    if (debug>1) printf( "objfile = '%s'\n", objfile) > co
  133.    cc = get_cl_param( "cc=", 0)
  134.    if (debug>1) printf( "cc = '%s'\n", cc) > co
  135.    ccf = get_cl_param( "ccf=", 0)
  136.    if (debug>1) printf( "ccf = '%s'\n", ccf) > co
  137.    # Omitting "incpath=xx" ok.. assume default directory for #include files
  138.    incpath = get_cl_param( "incpath=", 1)
  139.    if (debug>1) printf( "incpath = '%s'\n", incpath) > co
  140.    if (ARGV[1] == "__NOFILE__") {
  141.       if (debug)                    
  142.          printf( "\n__NOFILE__: Empty dependency list generated.\n") > co
  143.       else
  144.          printf( "  (of __NOFILE__)\n") > co
  145.       exit # Hop to END; srcfile is still an empty string & flist is empty
  146.    }
  147.    # 1st in list of ARGV[]'s is source file (FILENAME not set in BEGIN section)
  148.    fullsrcfile = ARGV[1]
  149.    # Split primary source file name into Path, Name, and Extension components
  150.    split_filename( fullsrcfile, srcfile, fname_chars)
  151.    if (debug)
  152.       printf( "\n Scanning primary source file '%s%s%s'\n",
  153.               srcfile["path"], srcfile["name"], srcfile["ext"]) > co
  154.    else
  155.       printf( "  (of %s%s%s)\n",
  156.               srcfile["path"], srcfile["name"], srcfile["ext"]) > co
  157.    if (debug>1) printf( "srcfile[\"path\"] = \"%s\"\n", srcfile["path"]) > co
  158.    if (debug>1) printf( "srcfile[\"name\"] = \"%s\"\n", srcfile["name"]) > co
  159.    if (debug>1) printf( "srcfile[\"ext\"] = \"%s\"\n", srcfile["ext"]) > co
  160. }
  161. #
  162. # Parser must deal with these abberations on the assumed form '#include "file".'
  163. #  a) '# include "file"'            c) '#include <file>'
  164. #  b) '#include"file"'              d) '#include "file"/* .. */'
  165. #                                   e) ' #include "file"'
  166. /^[ \t]*#[ \t]*include[ \t]*["<]/ {
  167.    if (debug>1) printf( "include: NR=%d, $0='%s', $1='%s'\n", NR, $0, $1) >co
  168.    incpath_first = extract_filename()  # into $1
  169.    if (debug>1) printf( " extracted filename = '%s'\n", $1) > co
  170.    # Check for nested inclusions by scanning included file for #include's
  171.    scanfile( $1, incpath, incpath_first)
  172. }
  173.  
  174. # Produce the dependency list printout
  175. END {
  176.    # Print primary (object) file name before list (left of colon),
  177.    # Print primary source file name (*.c) as first item in list.
  178.    printf( "%s: %s", objfile, fullsrcfile )
  179.    if (debug) printf( "%s: %s", objfile, fullsrcfile) >co
  180.    linelen = length(objfile) + 2 + length(fullsrcfile)
  181.  
  182.    for (i=1; i in flist; i++) {
  183.       # If line will be too long, put out a line-continuation char and newline
  184.       if (linelen + 1 + length(flist[i]) + length(linecont)  > 79) {
  185.          printf( "%s\n", linecont)
  186.          if (debug) printf( "%s\n", linecont) >co
  187.          # On the new line, tab out to underneath the first included file name
  188.          for (linelen=0; linelen<length(objfile)+1; ++linelen) {
  189.             printf(" ")
  190.             if (debug) printf(" ") >co
  191.          }
  192.       }
  193.       printf( " %s",  flist[i] )
  194.       if (debug) printf( " %s",  flist[i] ) >co
  195.       linelen += length(flist[i]) + 1
  196.       }
  197.  
  198.    # Build the compile command line's only parameter: the filename
  199.    #  depending on options specified in ccf command-line param
  200.    compile_comd_filename = sprintf( "%s%s%s",
  201.                                     ccf ~ /[Pp]/? srcfile["path"] : "",
  202.                                     ccf ~ /[Nn]/? srcfile["name"] : "",
  203.                                     ccf ~ /[Ee]/? srcfile["ext"]  : "" );
  204.    if (debug>1) 
  205.       printf( "\ncompile_comd_filename = \"%s\"", compile_comd_filename) > co
  206.    printf( "\n\t%s %s\n\n", cc, compile_comd_filename)
  207.    if (debug) printf( "\n\t%s %s\n\n", cc, compile_comd_filename) >co
  208. }
  209. #
  210. function scanfile( filename, incpath, incpath_first, # <- parameters
  211.                    glr, fullname1, fullname2) {      # <- local variables
  212.    # Looks for filename in directory incpath if incpath_first TRUE,
  213.    #  otherwise looks in default (current) directory path first.
  214.    # glr is a local variable: getline return value.
  215.    # fullname1, 2 are a local vars: path, filename glued together.
  216.    #  1 is first file to try, 2 contains other dir path option
  217.    #  If fullname1 not found but fullname2 is, fullname2 copied over fullname1
  218.    if (incpath_first) {
  219.       fullname1 = incpath filename;
  220.       fullname2 = filename
  221.    }
  222.    else {
  223.       fullname1 = filename; 
  224.       fullname2 = incpath filename
  225.    }
  226.    glr = getline <fullname1
  227.    if (glr == -1) { # Error opening file.. assume doesn't exist
  228.       # Try looking in the second choice of dir paths
  229.       glr = getline <fullname2
  230.       if (glr == -1) { # Error opening file.. assume doesn't exist
  231.          # Can't find filename in either directory path option
  232.          printf( "\007CINCDEP.AWK error:  cannot open file %s", fullname1) >co
  233.          if (incpath != "") printf( " nor %s\n", fullname2) >co
  234.          else               printf( "\n")                   >co
  235.          if (use_fnexist) # Put it in list anyway, if you say so
  236.             addfile( filename,  flist )
  237.          return # Can't scan this file
  238.       }
  239.       fullname1 = fullname2
  240.    }
  241.    # File has been found (its name is in fullname1) and opened.
  242.    # Add it to list of included files; if already there, don't scan it again.
  243.    already = addfile( fullname1, flist)
  244.    if (already)
  245.       return
  246.    while (glr == 1) {
  247.       if ( $0 ~ /^[ \t]*#[ \t]*include[ \t]*["<]/ ) {
  248.          incpath_first = extract_filename()
  249.          scanfile( $1, incpath, incpath_first)
  250.       }
  251.       glr = getline <fullname1
  252.    }
  253.    close( fullname1)
  254. }
  255. #
  256. function extract_filename(    retval) {
  257.    # Takes $0 as input; produces extracted filename in $1
  258.    # Returns TRUE if angle brackets (i.e. <file>) were used,
  259.    # 0 if "file" form was used.
  260.    # retval is a local variable: the return value.
  261.    if (debug>2)
  262.       printf( " extract_filename() entered: $0='%s', $1='%s'\n", $0, $1) > co
  263.    # Delete "#include" from $0; forces re-parsing, making $1 into
  264.    # (quoted) filename.  This solves a), b) and e).
  265.    gsub( /\t/, " "); # Replace all tabs w/SPs (field separator FS is space)
  266.    sub(/ *# *include */, "")
  267.    retval = (index($1,"<")==1) # 1 for <file>, 0 for "file"
  268.    if (debug>2)
  269.       printf( "  extract_filename(): incpath_first = %s\n",
  270.                    retval? "TRUE":"FALSE")                       > co
  271.    # Replace quote chars w/SPs to force what's after into $2; solves case d).
  272.    gsub( /[\"<>]/, " ")
  273.    if (debug>2)
  274.       printf( "  extract_filename() nuked [\"<>] -> '%s'\n", $1) > co
  275.    # Filename, stripped of either type of quote char, is now $1
  276.    return retval
  277. }
  278.  
  279. function addfile( filename, flist,    flix) {
  280.    # filename is a string to be added to flist[]
  281.    # flist[] is an array of strings to which addfile() appends a file name
  282.    # flix is a (local) index into flist[]
  283.    # Returns 1 if filename is already in list, 0 if it's a new entry.
  284.    for (flix=1; flix in flist; flix++)
  285.       if (flist[flix] == filename) # Already in list--
  286.          return 1                  #  don't add a duplicate
  287.    flist[flix] = filename
  288.    return 0
  289. }
  290. #
  291. function get_cl_param( parname,  # Which parameter ya lookin' fer? (e.g. "blah=")
  292.                        optional) # Is it an optional parameter?
  293. # Search the ARGV[] array for a command-line parameter beginning w/ parname
  294. # If optional is 0, aborts program if not found
  295. # Returns the string to the right of the '='
  296. {
  297.    for (i in ARGV)
  298.       if ( match(ARGV[i],parname) )
  299.          return substr( ARGV[i], length(parname)+1 )
  300.  
  301.    if (optional)
  302.       return ""
  303.  
  304.    printf("cincdep.awk usage error:  Command-line parameter '%s' missing\n",
  305.           parname                                                          ) >co
  306.    exit 1  # Abort
  307. }
  308. #
  309. function split_filename( name,        # In: The full file name
  310.                          shards,      # Out:array which receives ["path"],
  311.                                       #     ["name"] and ["ext"] members
  312.                          fname_chars, # In: Regular expression which matches
  313.                                       #     any single file-name character.
  314.                                       #     (must exclude directory separator)
  315.                          # Local variables:
  316.                          pnsep,       # Path/Name separator (1st char of Name)
  317.                          nesep)       # Name/Extension separator (1st Ext char)
  318.  
  319. # Split a file name into three components: Path, Name, and Extension 
  320. {
  321.    pnsep = match( name, fname_chars"*$")
  322.    shards["path"] = substr( name, 1, pnsep-1)
  323.    nesep = match( name, "\\."fname_chars"*$")
  324.    if (debug>2)
  325.       print "in \"" name "\", name starts @ " pnsep ", ext @ " nesep >co
  326.    if (nesep==0) {
  327.       shards["name"] = substr( name, pnsep)
  328.       shards["ext"] = ""
  329.    }
  330.    else {
  331.       shards["name"] = substr( name, pnsep, nesep-pnsep)
  332.       shards["ext"] = substr( name, nesep)
  333.    }
  334. }
  335.  
  336.